
package com.huaweicloud.saaslanding.install.applicationservice.impl;

import com.huaweicloud.saaslanding.basic.database.mybatis.conditions.query.QueryWrap;
import com.huaweicloud.saaslanding.basic.enumeration.TenantAppDomainStatusEnum;
import com.huaweicloud.saaslanding.basic.exception.BizException;
import com.huaweicloud.saaslanding.basic.exception.code.ExceptionCode;
import com.huaweicloud.saaslanding.basic.utils.CheckNullUtil;
import com.huaweicloud.saaslanding.dao.common.entity.app.AppTemplate;
import com.huaweicloud.saaslanding.dao.common.entity.deployedresource.CdnResource;
import com.huaweicloud.saaslanding.dao.common.entity.deployedresource.DnsResource;
import com.huaweicloud.saaslanding.dao.common.entity.deployedresource.WafHostResource;
import com.huaweicloud.saaslanding.dao.common.entity.tenant.TenantAppDomain;
import com.huaweicloud.saaslanding.dao.common.repository.app.AppTemplateBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.deployedresource.CdnResourceBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.deployedresource.DnsResourceBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.deployedresource.WafHostResourceBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.tenant.TenantAppDomainBaseMapper;
import com.huaweicloud.saaslanding.huawei.dns.api.RecordSetApi;
import com.huaweicloud.saaslanding.huawei.dns.enumeration.RecordSetStatusEnum;
import com.huaweicloud.saaslanding.huawei.dns.enumeration.RecordSetTypeEnum;
import com.huaweicloud.saaslanding.huawei.dns.request.RecordSetCreateRequest;
import com.huaweicloud.saaslanding.huawei.dns.response.RecordSetCreateResponse;
import com.huaweicloud.saaslanding.huawei.dns.response.RecordSetDetailResponse;
import com.huaweicloud.saaslanding.huawei.iam.api.IamApi;
import com.huaweicloud.saaslanding.install.applicationservice.DnsService;
import com.huaweicloud.saaslanding.install.properties.Elb;
import com.huaweicloud.saaslanding.install.properties.Endpoint;
import com.huaweicloud.saaslanding.install.properties.Iam;
import com.huaweicloud.saaslanding.install.util.DomainNameUtil;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * DNS云解析 实现类
 *
 * @since 2023-03-28
 */
@Slf4j
@Service
public class DnsServiceImpl implements DnsService {

    /**
     * 获取WAF防护域名状态，最大请求次数
     */
    private static final Integer MAX_REQUEST_TIME = 10;

    /**
     * 获取WAF防护域名状态，请求间隔
     */
    private static final Integer REQUEST_INTERVAL = 30000;

    @Resource
    private Iam iam;

    @Resource
    private Endpoint endpoint;

    @Resource
    private Elb elb;

    @Value("${saaslanding.platformAppName}")
    private String platformAppName;

    @Value("${saaslanding-install.waf.enabled:false}")
    private Boolean wafEnabled;

    @Value("${saaslanding-install.cdn.enabled:false}")
    private Boolean cdnEnabled;

    @Value("${saaslanding-install.dns.enabled:false}")
    private Boolean dnsEnabled;

    @Value("${saaslanding-install.dns.zoneId:null}")
    private String zoneId;

    @Resource
    private WafHostResourceBaseMapper wafHostResourceBaseMapper;

    @Resource
    private CdnResourceBaseMapper cdnResourceBaseMapper;

    @Resource
    private TenantAppDomainBaseMapper tenantAppDomainBaseMapper;

    @Resource
    private AppTemplateBaseMapper appTemplateBaseMapper;

    @Resource
    private DnsResourceBaseMapper dnsResourceBaseMapper;

    @Override
    public Long createRecordSet() throws InterruptedException {

        AppTemplate saasAppInfo = appTemplateBaseMapper.getSaasAppInfo();

        DnsResource dnsResource = new DnsResource();
        dnsResource.setTenantCode(platformAppName).setAppId(saasAppInfo.getId()).setIp(elb.getElbEip());

        if (!dnsEnabled) {
            String domainName = DomainNameUtil.getDomainName();
            dnsResource.setZoneName(domainName).setRecordValue(domainName);
            dnsResourceBaseMapper.insert(dnsResource);
            updateTenantAppDomain(dnsResource, TenantAppDomainStatusEnum.CONFIGURE_SUCCESSFUL);

            log.info("----------------DNS未开启，创建DNS记录集跳过---------------");
            return null;
        }

        log.info("----------------start：创建DNS记录集---------------");

        if (CheckNullUtil.isNull(zoneId)) {
            log.error("DNS公网域名ID为空！");
            throw new BizException(ExceptionCode.DNS_ZONE_ID_IS_NULL);
        }

        String dnsDomainName = generateDomainName();

        // 创建DNS记录集
        RecordSetCreateRequest recordSetCreateRequest = getRecordSetCreateRequest(dnsDomainName);

        Map<String, String> headers = IamApi.getIamHeader(iam.getEndpoint(), iam.getIamDomain(), iam.getIamUser(),
            iam.getIamPassword(), iam.getProjectId());

        RecordSetCreateResponse response =
            RecordSetApi.create(endpoint.getDns(), zoneId, recordSetCreateRequest, headers);

        dnsResource.setRecordSetId(response.getId())
            .setRecordValue(response.getRecords().get(0))
            .setZoneName(dnsDomainName)
            .setZoneId(zoneId)
            .setType(response.getType())
            .setStatus(response.getStatus());
        dnsResourceBaseMapper.insert(dnsResource);

        log.info("----------------end：创建DNS记录集---------------");

        TenantAppDomain tenantAppDomain = updateTenantAppDomain(dnsResource, TenantAppDomainStatusEnum.DNS_CONFIGURING);

        Boolean isSuccessful = isRecordSetCreatedSuccesful(response.getId(), headers, dnsResource);

        if (!isSuccessful) {
            updateTenantAppDomainStatus(tenantAppDomain, TenantAppDomainStatusEnum.DNS_CONFIGURE_FAILED);
            log.error("DNS解析记录集配置失败");
            throw new BizException(ExceptionCode.CONFIG_DNS_FAILED);
        }

        updateTenantAppDomainStatus(tenantAppDomain, TenantAppDomainStatusEnum.CONFIGURE_SUCCESSFUL);

        return dnsResource.getId();
    }

    @Override
    public void deleteRecordSet() {
        if (!dnsEnabled) {
            log.info("----------------DNS未开启，删除DNS记录集跳过---------------");
            return;
        }

        log.info("----------------start：删除DNS记录集---------------");
        AppTemplate saasAppInfo = appTemplateBaseMapper.getSaasAppInfo();
        List<DnsResource> dnsResources = dnsResourceBaseMapper.selectList(new QueryWrap<DnsResource>().lambda()
            .eq(DnsResource::getTenantCode, platformAppName)
            .eq(DnsResource::getAppId, saasAppInfo.getId())
            .eq(DnsResource::getIsDel, false));

        Map<String, String> headers = IamApi.getIamHeader(iam.getEndpoint(), iam.getIamDomain(), iam.getIamUser(),
            iam.getIamPassword(), iam.getProjectId());

        for (DnsResource resource : dnsResources) {
            if (StringUtils.isNotBlank(resource.getRecordSetId())) {
                RecordSetApi.delete(endpoint.getDns(), zoneId, resource.getRecordSetId(), headers);
            }
        }

        log.info("----------------end：删除DNS记录集---------------");

    }

    /**
     * 生成域名
     *
     * @return 域名
     */
    private String generateDomainName() {
        String domainName = DomainNameUtil.getDomainName();
        if (StringUtils.isEmpty(domainName)) {
            log.error("ISV域名为空！");
            throw new BizException(ExceptionCode.DOMAIN_IS_NULL);
        }

        return domainName.replace(domainName.substring(0, domainName.indexOf(".")), "*").concat(".");
    }

    /**
     * 生成DNS记录集创建请求体
     *
     * @param dnsDomainName DNS域名
     * @return 创建记录集请求体
     */
    @NotNull
    private RecordSetCreateRequest getRecordSetCreateRequest(String dnsDomainName) {
        RecordSetCreateRequest recordSetCreateRequest = new RecordSetCreateRequest();
        recordSetCreateRequest.setName(dnsDomainName);

        List<String> records = new ArrayList<>();

        if (!wafEnabled && !cdnEnabled) {
            // 若WAF和CDN都未开启，则创建A记录，记录值为ELB公网IP
            records.add(elb.getElbEip());
            recordSetCreateRequest.setType(RecordSetTypeEnum.A.getType()).setRecords(records);
        } else if (cdnEnabled) {
            // 若CDN开启，则创建CNAME记录，记录值为CDN加速域名CNAME
            List<CdnResource> cdnResources = cdnResourceBaseMapper.getByTenant(platformAppName);
            if (cdnResources == null || cdnResources.isEmpty()) {
                log.error("CDN加速域名信息为空！");
                throw new BizException(ExceptionCode.CDN_DOMAIN_IS_NULL);
            }
            records.add(cdnResources.get(0).getCname());
            recordSetCreateRequest.setType(RecordSetTypeEnum.CNAME.getType()).setRecords(records);
        } else {
            // 若CDN未开启，WAF开启，则创建CNAME记录，记录值为WAF防护域名CNAME
            List<WafHostResource> wafHostResources = wafHostResourceBaseMapper.getByTenantCode(platformAppName);
            if (wafHostResources == null || wafHostResources.isEmpty()) {
                log.error("WAF防护域名信息为空！");
                throw new BizException(ExceptionCode.WAF_DOMAIN_IS_NULL);
            }
            records.add(wafHostResources.get(0).getCname());
            recordSetCreateRequest.setType(RecordSetTypeEnum.CNAME.getType()).setRecords(records);
        }
        return recordSetCreateRequest;
    }

    /**
     * 更新租户应用域名
     *
     * @param dnsResource DNS云解析记录集信息
     * @return 租户应用域名
     */
    private TenantAppDomain updateTenantAppDomain(DnsResource dnsResource, TenantAppDomainStatusEnum statusEnum) {
        AppTemplate saasAppInfo = appTemplateBaseMapper.getSaasAppInfo();
        TenantAppDomain tenantAppDomain =
            tenantAppDomainBaseMapper.getByTenantAndApp(platformAppName, saasAppInfo.getId());
        tenantAppDomain.setDnsResourceId(dnsResource.getId())
            .setRecordType(dnsResource.getType())
            .setRecordValue(dnsResource.getRecordValue())
            .setStatus(statusEnum.getStatus());
        tenantAppDomainBaseMapper.updateById(tenantAppDomain);
        return tenantAppDomain;
    }

    private Boolean isRecordSetCreatedSuccesful(String recordSetId, Map<String, String> headers,
        DnsResource dnsResource) throws InterruptedException {
        for (int i = 0; i < MAX_REQUEST_TIME; i++) {
            Thread.sleep(REQUEST_INTERVAL);

            RecordSetDetailResponse response = RecordSetApi.get(endpoint.getDns(), zoneId, recordSetId, headers);
            String status = response.getStatus();

            dnsResource.setStatus(status);
            dnsResourceBaseMapper.updateById(dnsResource);

            if (RecordSetStatusEnum.ACTIVE.getStatus().equals(status)) {
                log.info("DNS记录集已生效");
                return true;
            } else if (RecordSetStatusEnum.DISABLE.getStatus().equals(status)
                || RecordSetStatusEnum.FREEZE.getStatus().equals(status)) {
                log.warn("DNS记录集不可用或冻结");
                return false;
            } else if (RecordSetStatusEnum.ERROR.getStatus().equals(status)) {
                log.error("DNS记录集创建失败");
                return false;
            } else if (RecordSetStatusEnum.PENDING_DELETE.getStatus().equals(status)) {
                log.warn("DNS记录集删除中");
                return false;
            } else {
                log.info("DNS记录集等待创建/更新");
            }
        }
        return false;
    }

    /**
     * 更新租户应用域名状态
     *
     * @param tenantAppDomain 租户应用域名
     * @param tenantAppDomainStatusEnum 租户应用域名状态
     */
    private void updateTenantAppDomainStatus(TenantAppDomain tenantAppDomain,
        TenantAppDomainStatusEnum tenantAppDomainStatusEnum) {
        tenantAppDomain.setStatus(tenantAppDomainStatusEnum.getStatus());
        tenantAppDomainBaseMapper.updateById(tenantAppDomain);
    }
}
